#include "Python.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define CMP_MAXLINE 512

int fac(int n)
{
    printf("c: fac: %d\n", n);
    if (n < 2) return(1); /* 0! == 1! == 1 */
    return (n)*fac(n-1); /* n! == n*(n-1)! */
}

char *reverse(char *s)
{
    register char t, /* tmp */
    *p = s, /* fwd */
    *q = (s + (strlen(s)-1)); /* bwd */

    printf("c: reverse: %s\n", s);

    while (p < q) /* if p < q */
    { /* swap & mv ptrs */
        t = *p;
        *p++ = *q;
        *q-- = t;
    }
    return s;
}

void print_it(char *str)
{
    printf("%s\n", str);
}

char *get_datetime_str(void)
{
    struct timeval te;
    gettimeofday(&te, 0); // get current time
    long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds

    long sec = milliseconds / 1000, milli = milliseconds % 1000;
	struct tm *time_stru;
    time_stru = localtime(&sec);
	char buf[CMP_MAXLINE];
	strftime(buf, CMP_MAXLINE, "%Y-%m-%d %H:%M:%S", time_stru);
	char buf2[CMP_MAXLINE * 2];
	snprintf(buf2, CMP_MAXLINE * 2, "%s.%03ld", buf, milli);
    char *ptr = strdup(buf2);
    return ptr;
}

void print_datetime(void) {
    char * ptr = get_datetime_str();
    print_it(ptr);
    free(ptr);
}

int main()
{
    char s[BUFSIZ];
    printf("4! == %d\n", fac(4));
    printf("8! == %d\n", fac(8));
    printf("12! == %d\n", fac(12));
    strcpy(s, "abcdef");
    printf("reversing 'abcdef', we get '%s'\n", \
    reverse(s));
    strcpy(s, "madam");
    printf("reversing 'madam', we get '%s'\n", \
    reverse(s));
    print_it("Print this 001.");
    char *ptr = get_datetime_str();
    print_it(ptr);
    sleep(1);
    print_it("Print this 002.");
    print_datetime();
    sleep(1);
    print_it("Print this 003.");
    ptr = get_datetime_str();
    print_it(ptr);
    sleep(1);
    print_it("Print this 004.");
    print_datetime();
    sleep(1);
    return 0;
}

static PyObject*
Extest_fac(PyObject *self, PyObject *args) {
    int res;
    int num;
    PyObject* retval;

    res = PyArg_ParseTuple(args, "i", &num);
    if (!res) {
        return NULL;
    }

    res = fac(num);

    retval = (PyObject*)Py_BuildValue("i", res);
    return retval;
}

static PyObject*
Extest_doppel(PyObject *self, PyObject *args) {
    int res;
    char *input_str;
    char *dup_str;
    char *result_str;
    
    PyObject* retval;

    res = PyArg_ParseTuple(args, "s", &input_str);
    if (!res) {
        return NULL;
    }

    dup_str = strdup(input_str);
    result_str = reverse(dup_str);

    retval = (PyObject*)Py_BuildValue("ss", input_str, result_str);
    free(dup_str);
    return retval;
}

static PyObject*
Extest_print_it(PyObject *self, PyObject *args) {
    int res;
    char *input_str;

    res = PyArg_ParseTuple(args, "s", &input_str);
    if (!res) {
        return NULL;
    }

    print_it(input_str);

    return Py_None; // return Py_None;
}

static PyObject*
Extest_get_datetime_str(PyObject *self, PyObject *args) {
    PyObject* retval;

    // 确保没有传入任何参数
    if (!PyArg_ParseTuple(args, "")) {
        printf("Extest_get_datetime_str: failed to parse args.");
        return NULL;
    }

    char *ptr = get_datetime_str();
    retval = (PyObject*)Py_BuildValue("s", ptr);
    
    return retval;
}

// warning: initialization of ‘PyObject * (*)(PyObject *, PyObject *)’ {aka ‘struct _object * (*)(struct _object *, struct _object *)’} from incompatible pointer type ‘PyObject * (*)(PyObject *)’ {aka ‘struct _object * (*)(struct _object *)’} [-Wincompatible-pointer-types]
// static PyObject*
// Extest_get_datetime_str_noargs(PyObject *self) {
//     PyObject* retval;

//     char *ptr = get_datetime_str();
//     retval = (PyObject*)Py_BuildValue("s", ptr);
    
//     return retval;
// }

static PyObject*
Extest_print_datetime(PyObject *self, PyObject *args) {
    // 确保没有传入任何参数
    if (!PyArg_ParseTuple(args, "")) {
        printf("Extest_get_datetime_str: failed to parse args.");
        return NULL;
    }
    char * ptr = get_datetime_str();
    print_it(ptr);
    free(ptr);

    return Py_None; // return Py_None;
}

static PyMethodDef
ExtestMethods[] = {
    {"fac", Extest_fac, METH_VARARGS, "Get factor of a number."},
    {"doppel", Extest_doppel, METH_VARARGS, "Reverse a string, get both the input and output."},
    {"print_it", Extest_print_it, METH_VARARGS, "Print a message."},
    {"get_datetime_str", Extest_get_datetime_str, METH_VARARGS, "Get a string representing the current datetime."},  // Why not METH_NOARGS?
    // {"get_datetime_str_noargs", Extest_get_datetime_str_noargs, METH_NOARGS, "Get a string representing the current datetime."},  // Why not METH_NOARGS?
    {"print_datetime", Extest_print_datetime, METH_VARARGS, "Print the current datetime."},  // Why not METH_NOARGS?
    {NULL, NULL, 0, NULL}
};

static PyModuleDef ExtestModule = {
    PyModuleDef_HEAD_INIT,
    "Extest",
    "Extest document.",
    -1,
    ExtestMethods
};

// void initExtest() {
//     Py_InitModule("Extest", ExtestMethods);
// }
PyMODINIT_FUNC
PyInit_Extest(void) {
    printf("@@@@ @@@@ @@@@ @@@@ %s @@@@ @@@@ @@@@ @@@@\n", "Module Extest is initializing");
    return PyModule_Create(&ExtestModule);
}
